1
with (javascript)

前一篇浅入正则(一)了解了实例化一个RegExp对象RegExp的原型方法RegExp的对象属性这些基础,大致知道正则怎么用,这一篇主要想了解一下正则怎么写。

元字符

元字符表随便就查得到,但这是会写正则最重要的基础,这里简单分成两类并按我的理解简单注释。

  • 运算符 & 限定符

{
    "\": "除语义类元字符中的使用,\ 为转义字符,比如想要匹配一个'(',直接使用'('会被认作分组的开始,需要使用\转义, '/\(/'",
    "^": {
        "开头": "在非'[]'中使用,表示匹配开头的意思,如果RegExp对象设置多行(m)属性,也会匹配换行符\n及\r之后的位置",
        "非": "在'[]'中,表示取反,例如:'/[^a]/',表示匹配不是a的字符"
    },
    "$": "匹配结束的位置,如果RegExp对象设置多行(m)属性,也会匹配换行符\n及\r之前的位置",
    "*": "任意的意思,表示匹配前面紧接着的子表达式0次或多次",
    "+": "表示匹配前面紧接着的子表达式1次或多次,也就是最少1次",
    "?": {
        1: "表示匹配前面紧接着的子表达式0次或1次,也就是最多1次",
        2: "非贪婪模式:前提是紧跟在其他限定符后面,比如'*'、'+'、'{n,m}'等,表示尽可能少的匹配,比如'{n,m}',例如: '/\d{4,6}?/g',假如使用这个正则test一段字符串'123456abc',则会尽可能少的匹配,也就是每次test只匹配4个,然后lastIndex被置为4,默认情况下为贪婪模式,每次会匹配6个,lastIndex会重置为6,如果无法匹配6个才会尝试匹配5个,4个"
    },
    "{n}": "表示匹配前面紧接着的子表达式n次,n为非负整数",
    "{n,}": "表示匹配前面紧接着的子表达式n次或更多次,也就是最少n次,n为非负整数",
    "{n,m}": "表示匹配前面紧接着的子表达式n次到m次,也就是最少匹配n次,最多匹配m次,n <= m 且n和m均为非负整数",
    ".": "除'\n'外的任意字符",
    "|": "或,例如'/a|bcd/'可以匹配'a'或者'bcd','/(a|b)cd/'可以匹配'acd'或者'bcd'",
    "-": "仅当在'[]'中可以表示连接符,'/[a-z]/'、'/A-Z/'、'/0-9/'分别表示a到z、A到Z、0到9,其他时间就表示中划线",
    "[]": "字符集合,表示中括号中的任意字符,比如'/[acb123ABC]/'表示匹配到'acb123ABC'中任意一个就可以",
    "()": "分组,和我们平常的加减运算中的()差不多,可以理解为确定优先级的意思,不过js正则中的()分组可以通过'$1 - $9'获取匹配结果中,每一个分组对应的匹配值,也就是'$1'也就是第一个分组的子表达式匹配成功的结果,例如:'abc123ef'.replace(/(\w)(\d){3}/g,'$2$1'),例子中,匹配(一个字母)紧接着(一个数字)循环了三次,'$1'就是后面紧接着数字的一个字母,也就是'c','$2'就是一个数字,也就是'(\d)'的最后一个匹配结果'3',因为数字循环了三次,所以三个数字会被替换成'$1',而c会被替换成'$2',结果就是'ab3cef'",
    "(?:)": "和()差不多一个意思,只是()中的分组内容不会被存储到'$1 - $9'的集合中",
    "(?=)": "正向前瞻,也就是前面的表达式紧接着的表达式,要符合(?=)的=后面跟着的表达式,例如:'/[a-z](?=\d)/',只会匹配后面紧接着数字的小写字母,但是数字并不会出现在匹配结果中且不会被存储到'$1 - $9'的集合中",
    "(?!)": "负向前瞻,也就是前面的表达式紧接着的表达式,要符合(?!)的 != 后面跟着的表达式,例如:'/[a-z](?!\d)/',只会匹配后面紧接着不是数字的小写字母,但是数字并不会出现在匹配结果中且不会被存储到'$1 - $9'的集合中"
}

基础的运算符和限定符无非就这几个,说到运算符就有优先级,上面的运算符的优先级为:

{
    "一级": "'\'",
    "二级": "'()'、'(?:)'、'(?=)'、'[]'",
    "三级": "'*'、"+"、'?'、'{n}'、'{n,}'、'{n,m}'",
    "四级": "'^'、'$'、'其他元字符及字符'",
    "五级": "'|'",
}
  • 语义类元字符

{
    "\b": "单词边界,也就是前面或者后面要跟着个空格",
    "\B": "非单词边界,也就是必须在单词中间,前后不能有空格",
    "\d": "匹配一个数字字符,等价于'[0-9]'",
    "\D": "匹配一个非数字字符,等价于'[^0-9]'",
    "\w": "匹配任意单词字符和下划线'_',等价于'[a-zA-Z0-9_]'",
    "\W": "匹配任意非单词字符及非下划线,等价于'[^a-zA-Z0-9_]'",
    "\s": "匹配任意空白字符,含空格、制表符、换页符等",
    "\S": "匹配非空白字符",
    //···还有很多不常用的,用时再查吧
}

前一篇笔记只解析了RegExp对象的原型方法,但是对于正则表达式,一些字符串的方法同样可以使用在正则上。

replace

replace也属于经常使用的一个方法,在具体到和正则表达式一起使用时是:stringObject.replace(RegExpObject,string|function),上例子:

var str1 = "2017-07-12";
var reg1 = /(\d{4})-(\d{2})-(\d{2})/g;
str1.replace(reg1, function(a,b,c,d,e){
    // a/b/c/d/e分别代表: 匹配结果/第一个分组匹配值/第二个分组匹配值/第三个分组匹配值/匹配成功的第一个字符的index值
    console.log(a,b,c,d);                //2017-07-12 2017 07 12,0
    return c + "/" + d + "/" + b;
})
console.log(str1);                       //07/12/2017
  • 需要注意的是,string是没有replaceAll的方法的,需要全局替换请在正在中设置全局属性g

match

math方法也是string的方法,match方法用法和exec很相似,同样返回数组。

  • 非全局正则调用,输出结果和exec相似,无匹配结果则返回null,有则返回数组,分别存放每一个子表达式匹配的结果,同时具有index和input两个属性:

var reg1 = /([a-zA-Z]\d)+([\u4e00-\u9fa5])+/;
//匹配 (大小写字母连着一个数字) 至少一次 (再连着汉字) 至少一次
var str1 = "a11B2老cd3李e45好";
var result = str1.match(reg1);
console.log(result);                     //["B2老", "B2", "老"]

这个结果和exec的结果完全一样;

  • 全局正则调用,数组只存放所有匹配的整个正则的子字符串,而不存放正则子表达式匹配的子字符串,也没有index和input的属性:

var reg2 = /([a-zA-Z]\d)+([\u4e00-\u9fa5])+/g;

//匹配 (大小写字母连着一个数字) 至少一次 (再连着汉字) 至少一次
var str2 = "a11B2老cd3李e45好";
var result1 = str2.match(reg2);
var result2 = str2.match(reg2);
var result3 = str2.match(reg2);

console.log(result1,result2,result3);                //["B2老", "d3李"] ["B2老", "d3李"] ["B2老", "d3李"]
console.log(result1.index,result2.input,result3);    //undefined undefined ["B2老", "d3李"]
执行多次均只输出每一个匹配完整正则的子字符串,同时也没有index和input的属性。

search

search方法返回的就是正则第一次匹配成功的开始位置,用法是stringObject.search(regexp);同时serch方法只需要匹配成功一次,不会重置laseIndex属性,每次都从字符串起始位置开始搜索,也会忽略全局搜索g

var reg3 = /([a-zA-Z]\d)+([\u4e00-\u9fa5])+/g;
var reg4 = /([a-zA-Z]\d)+([\u4e00-\u9fa5])+/g;
var str3 = "a11B2老cd3李e45好";
str3.search(reg3);        //3
str3.search(reg3);        //3
str3.search(reg4);        //3
str3.search(reg4);        //3
多次搜索和是否全局均不会影响搜索结果

不管多么复杂的正则,都是用本节中的元字符写出来的,而在复杂的正则,用法无非也就上节和这一节中所记录的方法。关键在于常用才能熟练,深入才能理解。这两节笔记只是浅入,在实践中摸索实践才能真的深入浅出,一起加油。


浅入正则(一)


前端老李
400 声望39 粉丝